#include "Subsystems/SubsystemAnimalEntities.h"
#include "Subsystems/SubsystemTimeOfDay.h"
#include "Subsystems/SubsystemTerrain.h"

#include "Network/Packages/MessagePackage.h"
#include "Network/Packages/EntityPackage.h"
#include "Network/PackageManager.h"
#include "Terrain/TerrainChunk.h"

#include "Managers/GameManager.h"
#include "Managers/EntityManager.h"
#include "Managers/ComponentsManager.h"

#include "Components/ComponentBlockEntity.h"
#include "Components/ComponentBody.h"
#include <fstream>

#include "Log.h"
#include "GameSingleton.h"
#include "GameRegistry.hpp"
#include "GameThreadPool.hpp"
#include "Game/EntityData.hpp"

using namespace PocketSurvival::Engine;

namespace PocketSurvival
{
    void SubsystemAnimalEntities::onTerrainChunkAdded(TerrainChunk *terrainChunk)
    {
        m_chunkAnimalMapkMutex.lock();
        // 查找动物记录
        auto it = m_chunkAnimalsMap.find(terrainChunk->coords);
        if (it != m_chunkAnimalsMap.end())
        {
            // 添加动物
            for (AnimalEntityData *animalEntityDataPtr : it->second)
            {
                m_addAnimalQueue.enqueue(animalEntityDataPtr);
            }
            m_chunkAnimalsMap.erase(it);
        }
        m_chunkAnimalMapkMutex.unlock();
    }

    void SubsystemAnimalEntities::onTerrainChunkRemoved(TerrainChunk *terrainChunk)
    {
        std::vector<AnimalEntityData*> animalEntityDataPtrVec;

        std::vector<entt::entity> entityVec;
        animalLock.lock();
        for (entt::entity entity : m_animalEntitiesSet)
        {
            entityVec.push_back(entity);
        }
        animalLock.unlock();


        for (entt::entity entity : entityVec)
        {
            ComponentBody *body = GameSingleton::gameRegistry->get<ComponentBody*>(entity);
            EntityData *entityData = GameSingleton::gameRegistry->get<EntityData*>(entity);

            Vector3 position;
            body->getPosition(position);

            Point3 point = position.toPoint3();
            Point2 coords{(point.posX >> 4), (point.posZ >> 4)};
            if (coords == terrainChunk->coords)
            {
                AnimalEntityData *animalEntityDataPtr = new AnimalEntityData;
                animalEntityDataPtr->entityName = entityData->ObjectName;
                animalEntityDataPtr->position = position;

                animalEntityDataPtrVec.push_back(animalEntityDataPtr);

                removeAnimalEntity(entity);
            }
        }



        m_chunkAnimalMapkMutex.lock();
        m_chunkAnimalsMap[terrainChunk->coords] = animalEntityDataPtrVec;
        m_chunkAnimalMapkMutex.unlock();
    }

    /**
     * @brief 创建动物实体
     * @param jsonData 方块实体的基础数据
    */
    void SubsystemAnimalEntities::createAnimalEntity(const nlohmann::json& jsonData)
    {
        entt::entity entity = GameSingleton::entityManager->createEntityByEssentitalData(jsonData);

        // 通知所有子系统，有新的实体添加
        // 这里的动物实体还没添加进表里面，所以有没有加锁倒是没啥问题
        for (auto &pair : GameSingleton::gameManager->subsystemMap)
        {
            pair.second->onEntityAdded(entity);
        }

        // 新添加的实体要告知所有客户端
        std::shared_ptr<EntityPackage> package = std::make_shared<EntityPackage>(entity, EntityPackageType::LoadOne);
        GameSingleton::packageManager->addSendPackageToQueue(package);

        animalLock.lock();
        m_animalEntitiesSet.insert(entity);
        animalLock.unlock();
    }

    /**
     * @brief 生成新的动物实体
     * @param entityName 实体名称
     * @param position 动物实体生成的位置
    */
    void SubsystemAnimalEntities::addAnimalEntity(const std::string &entityName, const Vector3 &position)
    {
        AnimalEntityData *animalEntityData = new AnimalEntityData;
        animalEntityData->entityName = entityName;
        animalEntityData->position = position;
        m_addAnimalQueue.enqueue(animalEntityData);
    }
    

    void SubsystemAnimalEntities::removeAnimalEntity(entt::entity entity)
    {
        m_removeAnimalSetMutex.lock();
        m_removeAnimalSet.insert(entity);
        m_removeAnimalSetMutex.unlock();
    }

    void SubsystemAnimalEntities::update(float dt)
    {
        AnimalEntityData *animalEntityData = nullptr;
        while(m_addAnimalQueue.try_dequeue(animalEntityData))
        {
            nlohmann::json essentialJson;
            if (GameSingleton::entityManager->tryGetEntityEssentialData(animalEntityData->entityName, essentialJson))
            {
                essentialJson["Components"]["Body"]["Position"] = animalEntityData->position;
                createAnimalEntity(essentialJson);
            }
            delete animalEntityData;
        }


        

        m_removeAnimalSetMutex.lock();
        m_removeAnimalSet.swap(m_removeAnimalSetCatch);
        m_removeAnimalSetMutex.unlock();


        for (entt::entity entity : m_removeAnimalSetCatch)
        {
            auto &subsystemMap = GameSingleton::gameManager->subsystemMap;
            for(auto &pair : subsystemMap)
            {
                pair.second->onEntityRemoved(entity);
            }

            GameSingleton::packageManager->addSendPackageToQueue(std::make_shared<EntityPackage>(entity, EntityPackageType::Remove));

            animalLock.lock();
            auto it = m_animalEntitiesSet.find(entity);
            if (it != m_animalEntitiesSet.end())
            {
                m_animalEntitiesSet.erase(it);
            }
            else
            {
                Log::Error("无法移除不存在的动物实体");
            }
            animalLock.unlock();

            GameSingleton::entityManager->destoryEntity(entity);
        }
        m_removeAnimalSetCatch.clear();


        if (m_subsystemTimeOfDay->periodicGameTimeEvent(5.0, 0.0))
        {
            GameSingleton::gameThreadPool->push_task([this]
            {
                std::vector<entt::entity> entityVec;
                animalLock.lock();
                for (entt::entity entity : m_animalEntitiesSet)
                {
                    entityVec.push_back(entity);
                }
                animalLock.unlock();
                

                for (entt::entity entity : entityVec)
                {
                    ComponentBody *body = GameSingleton::gameRegistry->get<ComponentBody*>(entity);

                    Vector3 position;
                    body->getPosition(position);

                    Point3 point = position.toPoint3();

                    TerrainChunk *chunk = m_subsystemTerrain->getTerrainChunk(point.posX >> 4, point.posZ >> 4);
                    if (chunk == nullptr)
                    {
                        removeAnimalEntity(entity);
                    }
                }
            });
        }
    }

    void SubsystemAnimalEntities::internalSave()
    {
        nlohmann::json saveJson = nlohmann::json::object();
        m_chunkAnimalMapkMutex.lock();
        for (auto it = m_chunkAnimalsMap.begin(); it != m_chunkAnimalsMap.end(); ++it)
        {
            if (it->second.size() > 0)
            {
                auto jsonIt = saveJson.find(it->first.toString());
                if (jsonIt == saveJson.end())
                {
                    saveJson[it->first.toString()] = nlohmann::json::array();
                    jsonIt = saveJson.find(it->first.toString());
                }

                for (const AnimalEntityData *animalEntityDataPtr : it->second)
                {
                    jsonIt.value().push_back({
                        { "Name", animalEntityDataPtr->entityName },
                        { "Position", animalEntityDataPtr->position }
                    });
                }
            }
        }
        m_chunkAnimalMapkMutex.unlock();

        std::vector<entt::entity> entityVec;
        animalLock.lock();
        for (entt::entity entity : m_animalEntitiesSet)
        {
            entityVec.push_back(entity);
        }
        animalLock.unlock();


        for (entt::entity entity : entityVec)
        {
            ComponentBody *body = GameSingleton::gameRegistry->get<ComponentBody*>(entity);
            EntityData *entityData = GameSingleton::gameRegistry->get<EntityData*>(entity);

            Vector3 position;
            body->getPosition(position);

            Point3 point = position.toPoint3();
            Point2 coords { point.posX >> 4, point.posZ >> 4 };

            saveJson[coords.toString()].push_back({
                { "Name",    entityData->ObjectName },
                { "Position",position         }
            });
        }

        std::string animalEntitiesFile = fmt::format("{}/AnimalEntities.json", GameSingleton::gameManager->getNowWorldPath());
        if(Utils::SaveTextFile(animalEntitiesFile, saveJson.dump()) == false)
        {
            Log::Error(fmt::format("无法保存动物实体数据到文件: {}", animalEntitiesFile));
        }
    }

    void SubsystemAnimalEntities::load(const nlohmann::json &json)
    {
        m_subsystemTimeOfDay = GameSingleton::singletonPtr->getSingleton<SubsystemTimeOfDay>();
        m_subsystemTerrain = GameSingleton::singletonPtr->getSingleton<SubsystemTerrain>();

        std::string jsonText;
        if(Utils::LoadTextFile(fmt::format("{}/AnimalEntities.json", GameSingleton::gameManager->getNowWorldPath()), jsonText) == true)
        {
            nlohmann::json animalEntitiesJson = nlohmann::json::parse(jsonText);

            for(const auto &jsonItem : animalEntitiesJson.items())
            {
                std::vector<std::string> coordsData;
                Utils::StringSplit(jsonItem.key(), ",", coordsData);

                Point2 coords;
                coords.posX = std::stoi(coordsData[0]);
                coords.posY = std::stoi(coordsData[1]);

                std::vector<AnimalEntityData*> animalEntityDataPtrVec;

                for (const nlohmann::json &animalJson : jsonItem.value())
                {
                    AnimalEntityData *animalEntityDataPtr = new AnimalEntityData;
                    animalEntityDataPtr->entityName = animalJson["Name"].get<std::string>();
                    animalEntityDataPtr->position = animalJson["Position"].get<Vector3>();
                    animalEntityDataPtrVec.push_back(animalEntityDataPtr);
                }
                m_chunkAnimalsMap[coords] = animalEntityDataPtrVec;
            }
        }
    }

    void SubsystemAnimalEntities::save(nlohmann::json &json)
    {
        // 交由线程池进行保存处理
        GameSingleton::gameSaveThreadPool->push_task(std::bind(&SubsystemAnimalEntities::internalSave, this));
    }

    static const std::string subsystemName = "AnimalEntities";
    const std::string& SubsystemAnimalEntities::getName() const
    {
        return subsystemName;
    }
    SubLoadEchelon SubsystemAnimalEntities::getLoadEchelon()
    {
        return SubLoadEchelon::Tenth;
    }
    SubUpdateType SubsystemAnimalEntities::getUpdateType()
    {
        return SubUpdateType::MainSingleUpdate;
    }
}